﻿#include "NetChannelHandel.h"
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#pragma comment(lib,"wsock32")
#include <winsock2.h>
#else
#include <errno.h>
#include <signal.h>
#endif

/*
#include <curl\include\win32\curl\curl.h>


void http_send()
{
	CURL *curl;
	CURLcode res;
	string cc;
	curl = curl_easy_init();

	if (curl)
	{
		curl_easy_setopt(curl, CURLOPT_URL, ""); //设置请求的地址  
		curl_easy_setopt(curl, CURLOPT_POST, true); //设置数据类型
		string caozuo = "";
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, caozuo.c_str()); //将操作代码，和连接的网站组合，一起发送！ 
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HallView::writehtml); //数据处理回调函数  
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cc);//缓冲的内存  
		curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000);    //设置连接超时时间    
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
		{
			CCDictionary* pDict = CCDictionary::createWithContentsOfFile("chines.xml");
			string mes = ((CCString*)pDict->objectForKey("networking"))->getCString();
			platform::showMsg(mes);
		}
		curl_easy_cleanup(curl);
	}
	else
	{
		CCLOG("curl is null");
	}

}
*/
std::string getLocationTime()
{
	struct tm *tm;
	time_t timep;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)  
	time(&timep);
#else  
	struct timeval tv;
	gettimeofday(&tv, NULL);
	timep = tv.tv_sec;
#endif  

	tm = localtime(&timep);
	int year = tm->tm_year + 1900;
	int month = tm->tm_mon + 1;
	int day = tm->tm_mday;
	int hour = tm->tm_hour;
	int minute = tm->tm_min;
	int second = tm->tm_sec;

	char sysTime[50] = { 0 };
	sprintf(sysTime, "%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
	//CCLOG("%s", sysTime);
	return  StringUtils::format("%s", sysTime);
}

int isIPAddr(const char* pStr)
{
	int bRet = 1;
	if (NULL == pStr) return -1;
	const char* p = pStr;
	for (; *p != '\0'; p++)
	{
		if ((isalpha(*p)) && (*p != '.'))
		{
			bRet = 0;
			break;
		}
	}
	return bRet;
}

//最大多少秒，连接上收不到数据就提示用户，重新登录
int maxIdleTimeInSeconds = 60 * 3;
//最大多少秒，连接上收不到数据就提示用户，选择重连
int hint2TimeInSeconds = 60;
//多长时间没有收到任何数据，提示用户
int hintTimeInSeconds = 30;
NetChannelHandel* instance = nullptr;
bool isTheard = true;
queue<ByteArray*> requestQueue;
queue<ByteArray*> responseQueue;
static std::mutex       s_requestQueueMutex;
static std::mutex       s_responseQueueMutex;

map<unsigned short, NetCallback> nSelect;
//CCScheduler* scheduler;
std::string m_host;
unsigned short m_port;
NetChannel netModel;
//时间
struct timeval lastHintUserTime;
struct timeval lastReceiveDataTime;
struct timeval now;

static std::mutex mtx; // 全局互斥锁.
static std::condition_variable cv; // 全局条件变量.

NetChannelHandel* NetChannelHandel::getInstance()
{
	if (!instance )	{
		instance = new (std::nothrow)  NetChannelHandel();
	}
	return instance;
}

void NetChannelHandel::setModel(NetChannel net)
{
	netModel = net;
	if (netModel == NetChannel::NET_SOCKET_CHANNEL)
	{
		//初始化数据分发定时器
		//CCScheduler *defaultScheduler = CCDirector::sharedDirector()->getScheduler();
		//scheduler = new CCScheduler();
		//defaultScheduler->scheduleUpdateForTarget(scheduler, 0, false);
		//scheduler->scheduleSelector(schedule_selector(NetChannelHandel::netWorkOperation), this, 0, false);
		CCLOG("initNetwork socket");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
		WORD wVersionRequested;
		WSADATA wsaData;
		wVersionRequested = MAKEWORD(2, 2);
		if (WSAStartup(wVersionRequested, &wsaData) != 0)
		{
			CCLOG("WSAStartup failed with error: %d\n");
			return;
		}
#endif	

		if (m_host.length() < 1 || m_port == 0) {
			CCLOG("The emergence of network connection failure or IP address and port error.");
			return;
		}

		struct hostent* hp;

		hp = gethostbyname(m_host.c_str());
		if (!hp){
			return;
		}

		int addr = isIPAddr(m_host.c_str());

		if (addr == 0) {//域名连接
			char IPStr[64] = { 0 };
			//SERVER_ADDRESS  Server Domain
			sprintf(IPStr, "%d.%d.%d.%d", (hp->h_addr_list[0][0] & 0x00ff),

				(hp->h_addr_list[0][1] & 0x00ff),

				(hp->h_addr_list[0][2] & 0x00ff),

				(hp->h_addr_list[0][3] & 0x00ff));
			m_host = IPStr;
		}


		int dwServerIP = inet_addr(m_host.c_str());
		unsigned short wPort = m_port;
		m_Socket = socket(AF_INET, SOCK_STREAM, 0);

		CCLOG("m_Socket=%d", m_Socket);
		if (m_Socket == -1)
		{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
			WSACleanup();
#endif
			return;
		}

		int nOptValue = 1;
		if (setsockopt(m_Socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&nOptValue, sizeof(nOptValue)))
		{
			return;
		}

		sockaddr_in SocketAddr;
		memset(&SocketAddr, 0, sizeof(SocketAddr));
		SocketAddr.sin_family = AF_INET;
		convertToByteStream(wPort);
		SocketAddr.sin_port = wPort;
		SocketAddr.sin_addr.s_addr = dwServerIP;
		memset(&(SocketAddr.sin_zero), 0, sizeof(SocketAddr.sin_zero));
		int iErrorCode = connect(m_Socket, (sockaddr*)&SocketAddr, sizeof(SocketAddr));
		CCLOG("errno1=%d", errno);
		CCLOG("m_Socket=%d", m_Socket);
		if (iErrorCode == -1)
		{
			std::string err("connect_failed!");
			CCLOG("%s,%d", err.c_str(), errno);
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
			WSACleanup();
#endif
			return;
		}
		//设置为非阻塞模式
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
		unsigned long lArgp = 1;
		if (ioctlsocket(m_Socket, FIONBIO, &lArgp))
		{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
			WSACleanup();
#endif
			return;
		}
#else
		int flags = fcntl(m_hSocket, F_GETFL, 0);
		flags |= O_NONBLOCK;
		if (fcntl(m_Socket, F_SETFL, flags)) {
			clear();
			return false;
		}
		//    flags  = fcntl(m_hSocket,F_GETFL,0);
		//    fcntl(m_hSocket,F_SETFL,flags&~O_NONBLOCK);    //设置成阻塞模式；
#endif
		std::string err("The connection is successful!");
		CCLOG("%s,%d", err.c_str(), errno);
	}
}

NetChannel NetChannelHandel::getModel()
{
	return netModel;
}

void NetChannelHandel::setAddr(std::string host, unsigned short port)
{
	m_host = host;
	m_port = port;
}

void NetChannelHandel::startThread()
{
	gettimeofday(&now, nullptr);
	lastHintUserTime = now;
	lastReceiveDataTime = now;
	auto t = std::thread(&NetChannelHandel::networkThread, (void *)instance);
	t.detach();
}

void NetChannelHandel::destroyInstance()
{
	CC_SAFE_RELEASE_NULL(instance);
}

NetChannelHandel::NetChannelHandel() 
{
	//初始化资源
	dispose();	
}

NetChannelHandel::~NetChannelHandel()
{
	CCLOG("%s","~NetChannelHandel");
	isTheard = false;
	//Director::getInstance()->getScheduler()->unscheduleAll();
	if (netModel == NetChannel::NET_SOCKET_CHANNEL)
	{
		if (m_Socket != -1)
		{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
			WSACleanup();
			(closesocket(m_Socket));
#else
			(close(m_hSocket));
#endif
			m_Socket = -1;
		}
	}
	if (netModel == NetChannel::NET_SOCKET_CHANNEL)
	{
		cocos2d::network::HttpClient::getInstance()->destroyInstance();
	}
	dispose();
	CCLOG("~SocketHandel");
}

void NetChannelHandel::dispose()
{
	while (!requestQueue.empty())
	{
		auto* byte = requestQueue.front();
		CC_SAFE_DELETE(byte);
		requestQueue.pop();
	}

	while (!responseQueue.empty())
	{
		auto* byte = responseQueue.front();
		CC_SAFE_DELETE(byte);
		responseQueue.pop();
	}

	nSelect.clear();
	m_host = "";
	m_port = 0;
	m_Socket = -1;
}

bool NetChannelHandel::onRequest()
{
	if (!isTheard)return false;
	//如果是HTTP，直接返回通过
	if (netModel == NetChannel::NET_HTTP_CHANNEL)
	{
		return true;
	}
	if (m_Socket==-1)
	{
		return true;
	}
	int iRetCode = 0;
	char  recvContent[RECVMAXLEN] = { 0 };
	
	memset(recvContent, 0, sizeof(recvContent));

	//设置读取头部固定(N)个字节(由服务器定义告知)
	iRetCode = recv(m_Socket, recvContent, 5, 0);//MSG_DONTWAIT
	gettimeofday(&now, nullptr);
	//CCLOG("....................SocketHandel::get()=%d", iRetCode);
	switch (iRetCode)
	{
	case -1:
	{
			   //CCLOG("....................errno=%d", errno);
			   switch (errno)
			   {
			   case EAGAIN:
			   case EINTR:
			   {
							 if (now.tv_sec - lastReceiveDataTime.tv_sec > maxIdleTimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
							 {
								 //                         restart();
								 //您的网络已经出问题了
							 }
							 else if (now.tv_sec - lastReceiveDataTime.tv_sec > hint2TimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
							 {
								 //"您的网络好像出问题了！
							 }
							 else if (now.tv_sec - lastReceiveDataTime.tv_sec > hintTimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
							 {//
								 CCLOG("....................Socket 15 second timeout");
								 //restart();
								 lastHintUserTime = now;
								 lastReceiveDataTime = now;
							 }
			   }
				   break;
			   default:				   
				   /**if (errno == EDEADLK) {
					   CCLOG("....................Socket deadlock");
				   }
				   else if (errno == ECONNRESET || errno == ENETDOWN || errno == EINTR)
				   {
					   CCLOG("....................Connection reset by the server=%d", errno);
				   }
				   else if (errno == ETIMEDOUT){
					   CCLOG("....................ETIMEDOUT");
				   }
				   else{
					   //CCLOG("....................＝%d", errno);
				   }*/

				   //CCLOG("........other error............%d", errno);
				   break;
			   }
	}
		break;
	case 0:
	{
			  if (now.tv_sec - lastReceiveDataTime.tv_sec > maxIdleTimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
			  {
				  //您的网络已经出问题了
			  }
			  else if (now.tv_sec - lastReceiveDataTime.tv_sec > hint2TimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
			  {
				  //                        restart();
				  //"您的网络好像出问题了！
			  }
			  else if (now.tv_sec - lastReceiveDataTime.tv_sec > hintTimeInSeconds && now.tv_sec - lastHintUserTime.tv_sec > hintTimeInSeconds)
			  {//
				  CCLOG(".......111111............Socket 15 second timeout");
				//  restart();
				  lastHintUserTime = now;
				  lastReceiveDataTime = now;
			  }
	}
		break;
	default:
	{
			   auto scheduler = Director::getInstance()->getScheduler();
			   lastHintUserTime = now;
			   lastReceiveDataTime = now;
			   ByteArray buff(iRetCode);
			   buff.copy(recvContent, iRetCode);
			   unsigned char zip = buff.read_char();
			   bool iszip = zip == 0 ? true : false;
			   if (iszip) {
				   CCLOG("iszip=%d", iszip);
			   }
			   char statusString[1024] = {};
			   sprintf(statusString, "[%s]SOCKET Status Code: %d, ip = %s:%d", getLocationTime().c_str(), errno, m_host.c_str(), m_port);
			   CCLOG(statusString);
			   unsigned int buffSize = buff.read_uint();  //读取有效数据包长度
			   while (buff.length() < buffSize)//根据服务器发送的流数据长度，不完整继续读取补齐数据
			   {
				   //修正处理粘包的可能,读取当前包有效长度
				   int size = buffSize - buff.length();
				   char*  tempContent = new char[size];;
				   memset(tempContent, 0, size);
				   iRetCode = recv(m_Socket, tempContent, size, 0);
				   if (iRetCode>0)
				   {
					   buff.copy(tempContent, iRetCode);
				   }
				   CC_SAFE_DELETE(tempContent);
			   }
			   if (true)
			   {
				   short resLength = buff.read_short();
				   for (unsigned int i = 0; i < resLength; i++)
				   {					  
					   unsigned short commandId = buff.read_ushort();
					   unsigned int dataLen = buff.read_uint();
					   auto* data = new ByteArray(dataLen);
					   data->setCmdType(commandId);
					   data->copy(buff.rd_ptr(), dataLen);
					   responseQueue.push(data);
				   }
				   scheduler->performFunctionInCocosThread(CC_CALLBACK_0(NetChannelHandel::netWorkOperation, instance));
			   }
			   
			//std::unique_lock < std::mutex > lck(mtx);
			//cv.notify_one(); // 唤醒线程.
	}
		break;
	}
	return true;
}


void* NetChannelHandel::networkThread(void* args)
{

	std::unique_lock <std::mutex> lck(mtx);
	auto* handle = (NetChannelHandel*)args; //SocketHandel::getInstance();
	int m_Socket = handle->m_Socket;
	while (handle->onRequest())
	{
		/*if (NetChannel::NET_HTTP_CHANNEL == netModel)
		{
			if (!isHttpBack)
			{
				//CCLOG("wait callback handle...");
				continue;
			}
		}
		isHttpBack = false;*/
		bool send=handle->sendMessage();
		if (send)
			if (NetChannel::NET_HTTP_CHANNEL == netModel)
				cv.wait(lck);//数据发送给成功后等待数据回收.
			else
				std::this_thread::sleep_for(std::chrono::milliseconds(150));										
	}
	return (void*)0;
}

//说明:可组合封包或者单例封包。
bool NetChannelHandel::warp(ByteArray* data)
{
	if (requestQueue.empty())
	{
		return false;
	}

	ByteArray array;
	unsigned short length = requestQueue.size();
	array.write_short(length);//包数量
	for (size_t i = 0; i < length; i++)
	{
		//并包
		auto* buff = requestQueue.front();
		requestQueue.pop();
		array.write_ushort(buff->getCmdType());
		short length = buff->length();
		array.write_short(length);
		array.copy(buff->base(), length);
		CC_SAFE_DELETE(buff);
	}
	data->write_short(array.length());
	data->copy(array.base(), array.length());
	return true;
}

int tag = 0;
bool NetChannelHandel::sendMessage()
{
	ByteArray data;
	if (netModel == NetChannel::NET_SOCKET_CHANNEL)
	{
		//添加解码头sock
		data.write_char('s');
		data.write_char('o');
		data.write_char('c');
		data.write_char('k');
	}	
	data.write_string("");//sid
	bool isSend =this->warp(&data);
	if (!isSend)
	{
		//CCLOG(".....warp=%d....", data.length());
		return false;
	}
	signed int bytes;
	signed int ret = 0;
	unsigned int	length = data.length();
	//CCLOG(".....warp=%d....",length);
	switch (netModel)
	{
	case NetChannel::NET_HTTP_CHANNEL:
	{
		 char url[30];
		 sprintf(url, "http://%s:%d", m_host.c_str(), m_port);
		 cocos2d::network::HttpRequest* request = new (std::nothrow) cocos2d::network::HttpRequest();
		 request->setUrl(url);
		 request->setRequestType(cocos2d::network::HttpRequest::Type::POST);
		 std::vector<std::string> headers;		
		 
		 headers.push_back("User-Agent=win");
		 headers.push_back("Content-Type=application/octet-stream;charset=utf-8");
		 headers.push_back("Connection=close");
		 //request->setHeaders(headers);
		 request->setResponseCallback(CC_CALLBACK_2(NetChannelHandel::onHttpRequest, this));
		 // write the post data
		 request->setRequestData(data.base(), length);
		 char msg[50] = { 0 };
		 sprintf(msg, "%d", tag++);
		 request->setTag(msg);
		 cocos2d::network::HttpClient*  httpClient = cocos2d::network::HttpClient::getInstance();
		 
		 httpClient->setTimeoutForConnect(10); //设置连接超时时间
		 httpClient->setTimeoutForRead(10);//设置发送超时时间
		 if (false)
		 {
		 //阻塞发送消息
			 httpClient->sendImmediate(request);
		 }
		 else
		 {	//异步发送消息
			 httpClient->send(request);
		 }
		 request->release();
	}
		break;
	case NetChannel::NET_SOCKET_CHANNEL:
	{
		if (m_Socket == -1)return  false;
		while (ret < length)
		{
			bytes = send(m_Socket, data.base() + ret, length - ret, 0);
			if (bytes == -1 || bytes == 0)
			{
				ret = -1;
				break;
			}
			ret += bytes;
		}

		//data = NULL;
		if (ret == -1)
		{
			CCLOG("send error!");
			return false;
		}
		else{
			CCLOG("send successful!");
		}
	}
		break;
	}
	return true;
}

void NetChannelHandel::netWorkOperation()
{
	while (!responseQueue.empty())
	{
		ByteArray* data = responseQueue.front();
		responseQueue.pop();
		map<unsigned short, NetCallback>::iterator itr = nSelect.find(data->getCmdType());
		if (itr != nSelect.end())
		{
			NetCallback _callback = itr->second;
			
			if (_callback)
			{
				_callback(data);				
			}			
		}
		CC_SAFE_DELETE(data);
	}
}

void NetChannelHandel::onHttpRequest(cocos2d::network::HttpClient *sender, cocos2d::network::HttpResponse *response)
{
	std::unique_lock < std::mutex > lck(mtx);
	cv.notify_one();
	if (!response)
	{
		return;
	}
	
	if (0 != strlen(response->getHttpRequest()->getTag()))
	{
		CCLOG("%s ------>oked", response->getHttpRequest()->getTag());
	}
	int statusCode = response->getResponseCode();
	char statusString[1024] = {};
	sprintf(statusString, "[%s]HTTP Status Code: %d, ip = %s:%d", getLocationTime().c_str(), statusCode, m_host.c_str(), m_port);
	CCLOG("%s", statusString);
	if (statusCode != 200) {
		return;
	}

	if (!response->isSucceed())
	{
		return;
	}
	std::vector<char> *buffer = response->getResponseData();
	ByteArray buff(buffer->size());// = new ByteArray();

	for (unsigned int i = 0; i < buffer->size(); i++)
	{
		buff.write_char((*buffer)[i]);
	}
//	CCLOG("bufferSize=%ld", buffer->size());

	unsigned char zip = buff.read_char();
	bool iszip = zip == 0 ? true : false;
	if (iszip) {
		CCLOG("iszip=%d", iszip);
	}

	unsigned int buffSize = buff.read_uint();  //读取有效数据包长度
	//CCLOG("buffSize=%d,%d", buff.length(), buffSize);

	short resLength = buff.read_short();

	for (unsigned int i = 0; i < resLength; i++)
	{
		unsigned short commandId = buff.read_ushort();
		unsigned int dataLen = buff.read_uint();
		auto* data = new ByteArray(dataLen);
		data->setCmdType(commandId);
		data->copy(buff.rd_ptr(), dataLen);
		responseQueue.push(data);		
	}
	this->netWorkOperation();
}

void NetChannelHandel::sendMsg(ByteArray* data)
{
	requestQueue.push(data);
}

void NetChannelHandel::addEventListeners(unsigned short commandID, NetCallback _callback)
{
	map<unsigned short, NetCallback>::iterator itr = nSelect.find(commandID);
	if (itr == nSelect.end())
	{
		nSelect.insert(std::pair<unsigned short, NetCallback>(commandID, _callback));
	}
}

void NetChannelHandel::removeEventListeners(unsigned short commandID)
{
	map<unsigned short, NetCallback>::iterator itr = nSelect.find(commandID);
	if (itr != nSelect.end())
	{
		NetCallback _callback = itr->second;		
		_callback = nullptr;
		nSelect.erase(itr);
	}
}


